/*
 *
 * Copyright (C) 2012 Mauricio Garcia
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "administration.h"

extern const int MAXPEERS;
extern char connectKey[64];
extern int connectKeyLen;
extern char disconnectKey[64];
extern int disconnectKeyLen;
extern char establishedKey[64];
extern int establishedKeyLen;
extern char unestablishedKey[64];
extern int unestablishedKeyLen;

int main(int argc, char *argv[]){
 
  if(argc != 2){
    printf("Parameters: port\n");
    exit(1);
  }
  
  struct sockaddr_in serverSockAddr, peerSockAddr;
  int sockServer;
  int sockPeer,sockPeerOther;
  int i,j,k,ntransp;

  // Server configuration
  serverSockAddr.sin_family = AF_INET;
  serverSockAddr.sin_port = htons( atoi(argv[1]) );
  serverSockAddr.sin_addr.s_addr = htonl( INADDR_ANY );
  sockServer = Socket( AF_INET, SOCK_STREAM, 0 );
  Bind(sockServer,(struct sockaddr*)&serverSockAddr,sizeof(serverSockAddr));
  Listen(sockServer,5);
  printf("Server is listening at: %s:%i\n",inet_ntoa(serverSockAddr.sin_addr),
					ntohs(serverSockAddr.sin_port));
  
  // Configure the socket's set
  fd_set socksSetRead;
  int fdMax = sockServer;
 
	// The time to wait in the select function 
  struct timeval timeout;
  timeout.tv_sec = 1;
  timeout.tv_usec = 0;  

  char bufferMsg[256];	
  char peerID[16];
  // The hash table
  struct nodeList *sockList[MAXPEERS];
  struct peer *tmpPeer;
  struct peer *tmpPeerOther;
  int sockArr[MAXPEERS];
  int npeers = 0;
	int sizePeerSockAddr = sizeof(peerSockAddr);

  for(i=0; i<MAXPEERS; i++){
    sockList[i] = NULL;
  }
  
  // Main loop of the server
  while(1){
    
    // Initialize the sockets set
    FD_ZERO(&socksSetRead);
    FD_SET(sockServer,&socksSetRead);  
    for(i=0; i<npeers; i++){
      FD_SET(sockArr[i],&socksSetRead);    
    }
    
    if(select(fdMax+1,&socksSetRead,NULL,NULL,&timeout) < 0){
      perror("Select error:");
    }
    
    // Check for all file descriptors
    for(i=0; i<=fdMax; i++){
      
      if(!FD_ISSET(i,&socksSetRead)){ // If i-th file descriptor is not ready
				continue;				
			}
	
			if(i == sockServer){// If the ith file descriptor is the server listener
			  // Accept a connection
			  sockPeer = Accept(sockServer,(struct sockaddr*)&peerSockAddr,
									    			&sizePeerSockAddr);
			  if(npeers+1 < MAXPEERS){
  	  		// Receive the ID
			    ntransp = Recv(sockPeer,peerID,sizeof(peerID),0);
  			  peerID[ntransp] = '\0';
  			  insertPeer(sockList,sockPeer,&peerSockAddr,peerID);
	    
  			  // Send message to the others
  			  memcpy(&connectKey[connectKeyLen],&sockPeer,sizeof(int));	    
			    memcpy(&connectKey[connectKeyLen+sizeof(int)],peerID,ntransp);	    
	    		for(j=0; j<npeers; j++){
				  	ntransp = Send(sockArr[j],connectKey,
				   	connectKeyLen+sizeof(int)+ntransp, 0 );
	    		}
	    
	    		// Send the list of already connected
				  for(j=0,k=0; j<npeers; j++){
	      		tmpPeer = getPeer(sockList,sockArr[j]);
				   	memcpy(&bufferMsg[k],connectKey,connectKeyLen);
				    k+=connectKeyLen;
	    		 	memcpy(&bufferMsg[k],&sockArr[j],sizeof(int)); 
		      	k+=sizeof(int);
	    		  memcpy(&bufferMsg[k],tmpPeer->id,tmpPeer->lenID);	    
				    k+=tmpPeer->lenID;
	    		}
	    		ntransp = Send(sockPeer,bufferMsg,k,0);
	    
	    		if(sockPeer > fdMax){
				  	fdMax = sockPeer;
	    		}
	    		// Ad the new file descriptor to the set
	    		sockArr[npeers] = sockPeer;
				  npeers++;
	    		printf("Connection accepted %s, npeers: %i\n",peerID,npeers);
	  		}
			}else{ // i-th file descriptor is a peer
	  		// Receive data from peer
				ntransp = Recv(i,bufferMsg,sizeof(bufferMsg),0);
				if(ntransp <= 0){
	    		if(ntransp < 0){ // Error
				  	perror("Receive error");
	    		}else{ // Connection closed
	    			tmpPeer = getPeer(sockList,i);
	    		  sockPeerOther = tmpPeer->peerOther;
	    	 		if(sockPeerOther != -1){
							tmpPeerOther = getPeer(sockList,sockPeerOther);
							tmpPeerOther->peerOther = -1;
	      		}
				    // Close the i-th file descriptor and remove it from the sockArr
	    			close(i);							
	    			for(j=0; j<=npeers; j++){
							if(sockArr[j] == i){
							  break;
							}    
	      		}
				    npeers--;
	    			for(k=j; k<=npeers; k++){
							sockArr[k] = sockArr[k+1];
	      		}
	      		printf("Conection closed with %s, npeers: %i\n",tmpPeer->id,npeers);      
				    // Send message to the others
				    memcpy(&disconnectKey[disconnectKeyLen],&i,sizeof(int));	    
				    memcpy(&disconnectKey[disconnectKeyLen+sizeof(int)],
				    tmpPeer->id,tmpPeer->lenID);    
				    for(j=0; j<npeers; j++){
							ntransp = Send(sockArr[j],disconnectKey,
			      								 disconnectKeyLen+sizeof(int)+tmpPeer->lenID, 0 );
	      		}
	      		removePeer(sockList,i);
	    		}
	  		}else{ // We have valid data
	   			// Receive the data from the i-th peer
	    		tmpPeer = getPeer(sockList,i);
				  if(memcmp(bufferMsg,establishedKey,establishedKeyLen) == 0){
	  	   		// Connection
				    memcpy(&sockPeerOther,&bufferMsg[establishedKeyLen],sizeof(int));
	  			  if(sockPeerOther == i) continue;
	  			  tmpPeerOther = getPeer(sockList,sockPeerOther);
	  			  if(!tmpPeerOther) continue;
	  			  printf("%s wants to connect with: %s\n",tmpPeer->id,tmpPeerOther->id);
	  			  if(tmpPeer->peerOther == -1 && tmpPeerOther->peerOther == -1 ){
							tmpPeer->peerOther = sockPeerOther;
							tmpPeerOther->peerOther = i;
							// Send the message to the i-th peer
							memcpy(&establishedKey[establishedKeyLen],&sockPeerOther,sizeof(int));	    
							memcpy(&establishedKey[establishedKeyLen+sizeof(int)],
										&tmpPeerOther->addr,sizeof(tmpPeerOther->addr));	    
							memcpy(&establishedKey[establishedKeyLen+sizeof(int)+
											sizeof(tmpPeerOther->addr)],&tmpPeerOther->id,tmpPeerOther->lenID);
							ntransp = Send(i,establishedKey,establishedKeyLen+sizeof(int)+
											sizeof(tmpPeerOther->addr)+tmpPeerOther->lenID, 0 );
							// Send the message to the other peer
							memcpy(&establishedKey[establishedKeyLen],&i,sizeof(int));
							memcpy(&establishedKey[establishedKeyLen+sizeof(int)],
										&tmpPeer->addr,sizeof(tmpPeer->addr));		    
							memcpy(&establishedKey[establishedKeyLen+sizeof(int)+
											sizeof(tmpPeer->addr)],&tmpPeer->id,tmpPeer->lenID);
							ntransp = Send(sockPeerOther,establishedKey,establishedKeyLen+
											sizeof(int)+sizeof(tmpPeer->addr)+tmpPeer->lenID, 0 );
							printf("%s connection established with %s\n",tmpPeer->id,tmpPeerOther->id);
      			}else if(tmpPeerOther->peerOther != -1){
							// The other peer is already connected
							memcpy(&unestablishedKey[unestablishedKeyLen],&tmpPeerOther->id,
		      					 tmpPeerOther->lenID);
							ntransp = Send(i,unestablishedKey,unestablishedKeyLen+
											       tmpPeerOther->lenID, 0 );
							printf("Not connected, %s is busy\n",tmpPeerOther->id);
	      		}
					}else if(memcmp(bufferMsg,unestablishedKey,unestablishedKeyLen) == 0){
      			//Disconnection
	     			printf("%s wants to disconnect\n",tmpPeer->id);
					  if(tmpPeer->peerOther == -1){
							printf("%s is not connected with noone\n",tmpPeer->id);
					  }else{
							sockPeerOther = tmpPeer->peerOther;
							tmpPeerOther = getPeer(sockList,sockPeerOther);
							printf("%s disconnecting with %s\n",tmpPeer->id,tmpPeerOther->id);
							tmpPeer->peerOther = -1;
							tmpPeerOther->peerOther = -1;
							// Send the message to the i-th peer
							memcpy(&unestablishedKey[unestablishedKeyLen],&tmpPeerOther->id,
		       						tmpPeerOther->lenID);
							ntransp = Send(i,unestablishedKey,
			      								 unestablishedKeyLen+tmpPeerOther->lenID, 0 );
							// Send the message to the other peer
							memcpy(&unestablishedKey[unestablishedKeyLen],&tmpPeer->id,
									     tmpPeer->lenID);
							ntransp = Send(sockPeerOther,unestablishedKey,
			      									unestablishedKeyLen+tmpPeer->lenID, 0 );
	      		}
					}else{
						printf("%s says: %s\n",tmpPeer->id,bufferMsg);
	      		sockPeerOther = tmpPeer->peerOther;
	      		if(sockPeerOther != -1){
							ntransp = Send(sockPeerOther,bufferMsg,ntransp, 0 );
	      		}
	    		}
	  		}/* if ntransp of the peer*/
			}/* if the i-th file descriptor is the server*/
		} /*for the all file descriptors*/
  } /*main loop*/
  for(i=0; i<MAXPEERS; i++){
   	freeList(&sockList[i]);
  }
  return 0;
} /*main*/
